﻿using System;
using System.Collections.Specialized;
using System.Configuration;
using System.Reflection;
using SimpleServiceBus.Endpoint.Management.Monitoring;
using SimpleServiceBus.Messages.Management.Monitoring;

namespace SimpleServiceBus.Configuration
{
    public class EndpointStatusProbeConfigurationElement : ConfigurationElement
    {
        [ConfigurationProperty("name", IsRequired = true)]
        public string Name
        {
            get { return (string) base["name"]; }
            set { base["name"] = value; }
        }

        [ConfigurationProperty("unitOfMeasurement", IsRequired = false)]
        public string UnitOfMeasurement
        {
            get { return (string) base["unitOfMeasurement"]; }
            set { base["unitOfMeasurement"] = value; }
        }

        [ConfigurationProperty("canAggregate", DefaultValue = true, IsRequired = false)]
        public bool CanAggregate
        {
            get { return (bool) base["canAggregate"]; }
            set { base["canAggregate"] = value; }
        }

        [ConfigurationProperty("dataType", DefaultValue = MeasurementDataType.Numeric, IsRequired = false)]
        public MeasurementDataType DataType
        {
            get { return (MeasurementDataType) base["dataType"]; }
            set { base["dataType"] = value; }
        }

        [ConfigurationProperty("properties")]
        protected NameValueConfigurationCollection ConfiguredProperties
        {
            get { return (NameValueConfigurationCollection) base["properties"]; }
            set { base["properties"] = value; }
        }

        [ConfigurationProperty("type")]
        protected string TypeName
        {
            get { return (string) base["type"]; }
        }

        public NameValueCollection ProbeProperties
        {
            get
            {
                var props = new NameValueCollection();

                if (ConfiguredProperties != null)
                {
                    foreach (NameValueConfigurationElement setting in ConfiguredProperties)
                    {
                        props.Add(setting.Name, setting.Value);
                    }
                }

                return props;
            }
        }

        public Type ProbeType
        {
            get
            {
                if (String.IsNullOrEmpty(TypeName))
                    return null;

                return Type.GetType(TypeName);
            }
        }

        public IEndpointStatusProbe CreateProbeFromConfiguration()
        {
            Type probeType = ProbeType;
            IEndpointStatusProbe probe = null;

            if (probeType != null)
            {
                if (!typeof (IEndpointStatusProbe).IsAssignableFrom(probeType))
                    throw new ConfigurationErrorsException(
                        "The type property of an EndpointStatusProbe must refer to an implementation of IEndpointStatusProbe.");

                //First, we'll see if a constructor exists to customize the configuration
                //process.
                ConstructorInfo ctor = probeType.GetConstructor(new[] {typeof (NameValueCollection)});
                if (ctor != null)
                    probe = ctor.Invoke(new object[] {ProbeProperties}) as IEndpointStatusProbe;
                else
                {
                    //In that case, let's get the default constructor and use reflection to
                    //configure the probe.
                    ctor = probeType.GetConstructor(new Type[] {});
                    if (ctor != null)
                    {
                        probe = ctor.Invoke(new object[] {}) as IEndpointStatusProbe;
                        foreach (string name in ProbeProperties.Keys)
                        {
                            //If required, convert XML style property naming to .NET style
                            string propName = name;
                            if (name.Length > 1)
                                propName = name[0].ToString().ToUpper() + name.Substring(1);

                            PropertyInfo prop = probeType.GetProperty(propName);
                            string valString = ProbeProperties[name];
                            if (String.IsNullOrEmpty(valString) && prop.PropertyType != typeof (string))
                            {
                                prop.SetValue(probe, null, new object[0]);
                            }
                            else
                            {
                                Type propType = prop.PropertyType;
                                object val = Convert.ChangeType(valString, propType);
                                prop.SetValue(probe, val, new object[0]);
                            }
                        }
                    }
                }
            }

            if (probe != null)
            {
                probe.Name = Name;
                probe.UnitOfMeasurement = UnitOfMeasurement;
                probe.DataType = DataType;
            }

            return probe;
        }
    }

    public class EndpointStatusProbeConfigurationCollection : ConfigurationElementCollection
    {
        public EndpointStatusProbeConfigurationElement this[int index]
        {
            get { return (EndpointStatusProbeConfigurationElement) BaseGet(index); }
            set
            {
                if (BaseGet(index) != null)
                {
                    BaseRemoveAt(index);
                }
                base.BaseAdd(index, value);
            }
        }

        public override ConfigurationElementCollectionType CollectionType
        {
            get { return ConfigurationElementCollectionType.BasicMap; }
        }

        protected override string ElementName
        {
            get { return "probe"; }
        }

        protected override ConfigurationElement CreateNewElement()
        {
            return new EndpointStatusProbeConfigurationElement();
        }

        protected override object GetElementKey(ConfigurationElement element)
        {
            return ((EndpointStatusProbeConfigurationElement) element).Name;
        }
    }
}